# -*- coding: utf-8 -*- 

#########################################################################
## This is a samples controller
## - index is the default action of any application
## - user is required for authentication and authorization
## - download is for downloading files uploaded in the db (does streaming)
## - call exposes all registered services (none by default)
#########################################################################  

def index():

    treeInfo = { 'source': {}, 'piece': {} }

    if auth.is_logged_in():

        for row in db( (db.sourceTreeMapping.user_id == auth.user.id) &
                       (db.sourceTreeMapping.sourceTree == db.tree.id) ).select( db.tree.id, db.tree.name, db.tree.comment ) :
            treeInfo['source'][row.id] = { 'name': row.name, 'comment': row.comment }
        
        for row in db( (db.sourceTreeMapping.user_id == auth.user.id) &
                       (db.sourceTreeMapping.sourceTree == db.pieceTreeMapping.sourceTree) &
                       (db.pieceTreeMapping.pieceTree == db.tree.id) ).select( db.tree.id, db.tree.name, db.tree.comment ) :
            treeInfo['piece'][row.id] = { 'name': row.name, 'comment': row.comment }

    return dict( treeInfo = treeInfo )


def addNewick():

    import datetime, cPickle

    tree = None

    try:
        #newick=local_import('newick',force=True) 
        import applications.tred.modules.newick as newick
        reload(newick)
        tree = newick.parse( request.vars.newickString )
    except:
        return 'parseError'

    treeId = insertTree( { 'name': request.vars.newickName, 'comment': request.vars.newickComment, 'tree': tree } ).id

    db.sourceTreeMapping.insert( user_id = auth.user.id, sourceTree = treeId )

    insertTreeNodes( { 'node': tree, 'treeId': treeId, 'parent': None } )

    return treeId


def insertTree( p ):
    
    import datetime, cPickle

    return db.tree.insert( name = p['name'], comment = p['comment'],
                           creationDate = datetime.datetime.now(),
                           treeObj = cPickle.dumps( p['tree'] ) )



def insertTreeNodes( p ):

    nodeId = db.treeNode.insert( parent = p['parent'], tree = p['treeId'], nodeId = p['node'].id  ).id

    if p['node'].label:
        db.treeNodeMeta.insert( key = 'label', value = p['node'].label, node = nodeId )
    
    if p['node'].isroot:
        db.treeNodeMeta.insert( key = 'root', value = 'True', node = nodeId )
    
    if p['node'].length:
        db.treeNodeMeta.insert( key = 'length', value = p['node'].length, node = nodeId )

    for child in p['node'].children:
        insertTreeNodes( { 'node': child, 'treeId': p['treeId'], 'parent': nodeId } )


def viewer():

    import math, cPickle
    import applications.tred.modules.newick as newick
    import applications.tred.modules.auto_collapse as collapse
    reload(newick)
    reload(collapse)

    tree = cPickle.loads( db( db.tree.id == int(request.args[0]) ).select()[0].treeObj )

    collapsed = { }
    collapse.auto_collapse( tree, collapsed, [ ], int(request.args[1]) )

    removeCollapsed( { 'node' : tree, 'collapsed' : collapsed } )

    return dict( json = tree.emit_json(), treeId = request.args[0] )


def saveClade():

    import applications.tred.modules.newick as newick
    reload(newick)
    import datetime

    tree = getTreeObj( request.vars.treeId )

    cladeToSave = tree.findNodeById( int(request.vars.nodeId) )
    
    f = open( '/tmp/debug', 'w' )
    f.write( str(tree) )

    pieceTreeId = insertTree( { 'name': request.vars.name, 'comment': request.vars.comment, 'tree': cladeToSave } ).id

    db.pieceTreeMapping.insert( sourceTree = int(request.vars.treeId), pieceTree = pieceTreeId, sourceNodeId = request.vars.nodeId  )
    
    insertTreeNodes( { 'node': cladeToSave, 'treeId': pieceTreeId, 'parent': None } )


def getTreeObj( treeId ):

    import cPickle

    return cPickle.loads( db( db.tree.id == treeId ).select()[0].treeObj )



def grafter():

    import cPickle

    trees = { }

    for id in request.args:
        trees[int(id)] = cPickle.loads( db( db.tree.id == int(id) ).select()[0].treeObj ).emit_json()

    return dict( trees = trees )



##############################
    

def mrca():
        
    import applications.tred.modules.newick as newick
    reload(newick)

    treeId = int(request.vars.treeId)
    nodeId = int(request.vars.nodeId)
    tree = session.trees[treeId]['tree']

    newRoot = tree.findNodeById(nodeId)
    newRoot.isRoot = True

    session.trees[treeId]['tree'] = newRoot
    session.trees[treeId]['string'] = newick.to_string(newRoot)


def prune():
    
    import applications.tred.modules.newick as newick
    reload(newick)
    
    treeId = int(request.vars.treeId)
    nodeIds = request.vars.nodeIds.split(',')
    tree = session.trees[treeId]['tree']

    for id in nodeIds:
        nodeToPrune = tree.findNodeById(int(id))
        nodeToPrune.prune()
    
    session.trees[treeId]['string'] = newick.to_string(tree)


def editLabel():

    import applications.tred.modules.newick as newick
    reload(newick)
    
    treeId = int(request.vars.treeId)
    nodeId = int(request.vars.id)
    tree = session.trees[treeId]['tree']

    nodetoUpdate = tree.findNodeById( nodeId )
    nodetoUpdate.label = request.vars.label

    session.trees[treeId]['string'] = newick.to_string(tree)


def getNewick():
    import random
    randomInt = random.randrange(10000)
    f = open( ''.join( [ '/tmp/', str(randomInt), '.newick' ] ), 'w' )
    f.write( session.trees[int(request.vars.treeId)]['string'] )
    f.close()
    return randomInt

def updateNewick():
    import re
    newickString = db( db.userNewick.id == request.vars.treeId ).select()[0].newick
    toBeReplaced = re.compile( ''.join( [ '(\W)', request.vars.oldName, '(\W)' ] ) )
    newickString = toBeReplaced.sub( ''.join( [ '\\1', request.vars.newName, '\\2' ] ), newickString )
    db( db.userNewick.id == request.vars.treeId ).update( newick = newickString )


def save():
    import urllib,re,random
    svgFileHeader = '<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">'
    stripOut = re.compile( '<desc>.*</desc>')
    svgContent = stripOut.sub( '', urllib.unquote( request.vars.svg ) )
    randomInt = random.randrange(10000)
    f = open( ''.join( [ '/tmp/', str(randomInt), '.svg' ] ), 'w' )
    f.write( ''.join( [ svgFileHeader, svgContent ] ) )
    f.close()
    return randomInt


def receive():
    
    if request.args[0] == 'svg':
        response.headers['Content-Type'] = 'text/xml'
        response.headers["Content-Disposition"] = 'attachment;filename=svgfile.svg'
    else:
        response.headers['Content-Type'] = 'text/plain'
        response.headers["Content-Disposition"] = 'attachment;filename=newickfile.txt'

    f = open( ''.join( [ '/tmp/', request.args[1], '.', request.args[0] ] ), 'r')
    result = f.read()
    f.close()
    return result

def remove():
    import os
    os.remove( ''.join( [ '/tmp/', request.vars.id, '.', request.vars.type ] ) )


def delete():
    db( db.tree.id == int(request.vars.id) ).delete()
    db( db.treeNode.tree == int(request.vars.id) ).delete()
    db( db.sourceTreeMapping.sourceTree == int(request.vars.id) ).delete()
    return 1
        

def removeCollapsed( params ):

    for child in params['node'].children:
        if child.id in params['collapsed']:
            child.isExpandable = True
            child.children = []
        removeCollapsed( { 'node' : child, 'collapsed' : params['collapsed'] } ) 


def findNode( params ):
   
    if( params['node'].id == params['nodeId'] ):
        return params['node']
    else:
        for child in params['node'].children:
            node = findNode( { 'node' : child, 'nodeId' : params['nodeId'] } )
            if node:
                return node



def getChildren():

    node = findNode( { 'node': session.trees[ int(request.vars.treeId) ]['tree'], 'nodeId': int( request.vars.cladeId ) } )

    # need to figure out how to deep copy a Node instance :/    

    return node.emit_json()




def expandClade():

    import cPickle
    import applications.tred.modules.auto_collapse as collapse

    pkl_file = open( ''.join( [ '/tmp/', session.trees[ int(request.vars.treeId) ]['treeIndex'], '.tree' ] ), 'rb' )
    tree = cPickle.load(pkl_file)

    visibleList = session.trees[ int(request.vars.treeId) ]['visibleList']
    visibleList.append( int(request.vars.cladeId ) )
    
    f = open('/tmp/debug','w')

    expandingClade = findNode( { 'node': tree, 'nodeId': int( request.vars.cladeId ) } )

    for child in expandingClade.children:
        visibleList.append( child.id )

    while (expandingClade.parent):
        visibleList.append( expandingClade.parent.id )
        expandingClade = expandingClade.parent
   
    f.write( str( visibleList ) )

    collapsed = { }
    collapse.auto_collapse( tree, collapsed, visibleList, int(request.vars.maxTips) )

    removeCollapsed( { 'node' : tree, 'collapsed' : collapsed } )

    return tree.emit_json()


def collapseClade():
    return 1

    
def user():
    """
    exposes:
    http://..../[app]/default/user/login 
    http://..../[app]/default/user/logout
    http://..../[app]/default/user/register
    http://..../[app]/default/user/profile
    http://..../[app]/default/user/retrieve_password
    http://..../[app]/default/user/change_password
    use @auth.requires_login()
        @auth.requires_membership('group name')
        @auth.requires_permission('read','table name',record_id)
    to decorate functions that need access control
    """
    return dict(form=auth())

def download():
    """
    allows downloading of uploaded files
    http://..../[app]/default/download/[filename]
    """
    return response.download(request,db)


def call():
    """
    exposes services. for example:
    http://..../[app]/default/call/jsonrpc
    decorate with @services.jsonrpc the functions to expose
    supports xml, json, xmlrpc, jsonrpc, amfrpc, rss, csv
    """
    session.forget()
    return service()
